/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/
/* Example source code to show how the callback function can be used to
 * download data into a chunk of memory instead of storing it in a file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <curl/curl.h>

#include <pthread.h> //using multithread

#include <libusb-1.0/libusb.h>

#include "ASC16.h"
#include "HZK16.h"

/* You may want to change the VENDOR_ID and PRODUCT_ID
 * depending on your device.
 */
#define VENDOR_ID      0x0483   // Arduino LLC
#define PRODUCT_ID     0x5740   // Arduino Leonardo

#define ACM_CTRL_DTR   0x01
#define ACM_CTRL_RTS   0x02

/* We use a global variable to keep the device handle
 */
static struct libusb_device_handle *devh = NULL;

/* The Endpoint address are hard coded. You should use lsusb -v to find
 * the values corresponding to your device.
 */
static int ep_in_addr  = 0x81;
static int ep_out_addr = 0x01;

struct MemoryStruct {
  char *memory;
  size_t size;
  int match;
};

//we need a struct to record mdata and the property
struct stmdata {
  unsigned char mdata[12800]; //mdata
  int datanum; //num of mdata
  int fontsize;
  int startx;  //arear startx
  int starty;  //arear starty
  int endx;   //arear endx
  int endy;   //arear endy
  int mod;    // 0 static;
  int moffset;//offset
  int valign;
  int halign;
  int speed;
};

//main thread mutex tramdata
//main thread copy stockmdata to tramdata
//main thread unmutex tramdata

//print thread mutex tramdata
//print thread copy tramdata to ledmdata
//print thread unmutex tramdata 
struct stmdata tramdata;

pthread_mutex_t mutmdata;


static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  mem->match = memcmp(mem->memory, contents, (int)realsize);
  if(mem->match == 0) return realsize;

  mem->memory = realloc(mem->memory, realsize + 1);
  if(mem->memory == NULL) {
    /* out of memory! */
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }

  memcpy(mem->memory, contents, realsize);
  mem->size = realsize;
  mem->memory[mem->size] = 0;

  return realsize;
}

// add the mdata
// mword ，ascii or gbk2312 code
//pmdata, the pointer to store the code data
//datanum, how many chars store
void addmdata(unsigned int mcode, struct stmdata* mdata ) {
  //GBK
  if(mcode > 0x80) {
    unsigned int qh, wh, gbkoffset, i, j;
    qh = (mcode>>8) - 0xa0; //获得区码
    wh = (mcode & 0x00ff) - 0xa0; //获得位码 
    gbkoffset = (94*(qh-1)+(wh-1))*32; //得到偏移位置 
    //because in gbk
    //gbk0 gbk1
    //.........
    //gbk30 gbk31
    
    //but in the mdata
    //mdata0    mdata16
    //...........
    //mdata15   mdata31
    //we need a convert
    for(j=0; j<2; j++) {
      for(i=0; i<16; i++) {
        memcpy(mdata->mdata + mdata->datanum, GBK2312_8x16_Table+gbkoffset+2*i+j,1);
        (mdata->datanum)++;
      }
    }
    
  }
  //ASCII
  else {
    memcpy(mdata->mdata + mdata->datanum,ASCII8x16_Table+mcode*16,16);
    mdata->datanum += 16;
  }
  
}

// send date thread 
void *thr_send_date(void *data) {
  struct stmdata ledmdata;
  ledmdata.datanum =0;
  int harea; //lenght of area
  int varea; //heigh of area
  int ledupdate = 0;
  //screen is 128*64 bit
  //x 16byte
  //y 64line
  
  //creen matrix data 128*64 bit
  unsigned char screenmdata[16*64];
  //creen print data 128*64 bit, send to zh-u0
  unsigned char screenpdata[16*64];
  //byte00  ....  byte960
  //...
  //bute63  ....  byte1023
  
  //read the screendata, edit it, and write it back
  unsigned char tmpmdata;
  int updatenum;
  
  int i, j;
  
  for(i=0; i<16; i++) {
    for(j=0; j<64; j++) {
      screenmdata[i*64+j] = 0x00;
    }
  }
  
  while(1) {
    pthread_mutex_lock(&mutmdata);
    if(tramdata.datanum > 0) {
      memcpy(&ledmdata, &tramdata, sizeof(tramdata));
      ledupdate = 1;
      tramdata.datanum = 0;
    }
    pthread_mutex_unlock(&mutmdata);
    
    //check data
    //fit data
    //creat screendata (new data or run left)
    if(ledupdate) {
      if(ledmdata.startx < 0)  ledmdata.startx = 0;
      if(ledmdata.starty < 0)  ledmdata.starty =0;
      if(ledmdata.endx < 0)  ledmdata.endx = 0;
      if(ledmdata.endy < 0)  ledmdata.endy = 0;
    
      if(ledmdata.startx > 127)  ledmdata.startx = 127;
      if(ledmdata.starty > 127)  ledmdata.starty = 127;
      if(ledmdata.endx > 127)  ledmdata.endx = 127;
      if(ledmdata.endy > 127)  ledmdata.endy = 127;
    
      if(ledmdata.startx > ledmdata.endx) ledmdata.endx = ledmdata.startx;
      if(ledmdata.starty > ledmdata.endy) ledmdata.endy = ledmdata.starty;
    
      //run left
      if(ledmdata.mod == 1) {
        //heigh of area lower than font
        if((ledmdata.starty + ledmdata.fontsize) > ledmdata.endy) {
          varea = ledmdata.endy - ledmdata.starty + 1;
        }
        
        //heigh of area higher than font
        else {
          varea = ledmdata.fontsize;
          //top
          if(ledmdata.valign == 0) ledmdata.endy = ledmdata.starty + varea-1;
          //buttom
          else if(ledmdata.valign == 1) ledmdata.starty = ledmdata.endy - varea+1;
          //center and default
          else {
            ledmdata.starty = ledmdata.starty + (ledmdata.endy - ledmdata.starty -varea)/2;
            ledmdata.endy = ledmdata.starty + varea-1;
          }
        }
      }
      ledupdate = 0;
    }
    
    //bit startx + i, starty + j inscreen
    // in which byte ((startx + i)/8) * 64 + (starty + j)
    //in which bit (startx + i)%8
    
    //corresponding bit in mdata
    // in which byte  if ((i+offset)/8) * fontsize < datanum
    //              ((i+offset)/8) * fontsize +j
    //                else
    //              ((i+offset)/8) * fontsize +j - datanum 
    //              
    //in which bit (i+offset)%8
    
    //read the corresponding bit in mdata
    //write the value to the bit startx + i, starty + j inscreen
    
    updatenum = 0;
    for(j=0; (ledmdata.starty + j) <= ledmdata.endy; j++) { //each row in area
      for(i=0; (ledmdata.startx + i) <= ledmdata.endx; i++) { //each line in area
        //corresponding byte
        if (((i+ledmdata.moffset)/8) * ledmdata.fontsize < ledmdata.datanum) 
          tmpmdata = ledmdata.mdata[((i+ledmdata.moffset)/8) * ledmdata.fontsize +j];
        else 
          tmpmdata = ledmdata.mdata[((i+ledmdata.moffset)/8) * ledmdata.fontsize +j - ledmdata.datanum];
        if(tmpmdata & (0x01 << (7-((i+ledmdata.moffset)%8)))){ //if the bit is 1
          //bit set 1
          screenmdata[((ledmdata.startx + i)/8) * 64 + (ledmdata.starty + j)] |= (0x01 << (7-((ledmdata.startx + i)%8)));
        }
    
        else {
          //bit set 0
          screenmdata[((ledmdata.startx + i)/8) * 64 + (ledmdata.starty + j)] &= ~(0x01 << (7-((ledmdata.startx + i)%8)));
        }
      updatenum++;
      }
    }
    //ok, we get the all 128*64 screen data in screenmdata[]
    //in led screen the data format is 
    //address 00, connect1 is 
    //screenmdata[12] screenmdata[8] screenmdata[4] screenmdata[0] ...screenmdata[64*15+12] screenmdata[64*15+8] screenmdata[64*15+4] screenmdata[64*15+0]
    //each risk edge of clk send a bit
    //so we need screenmdata[1024] to stor data in this format
    //data address 00, connect1
    for(j=0; j<16; j++){
      for(i=0; i<4; i++) {
        screenpdata[i+4*j] = screenmdata[12-4*i+64*j];
      }
    }
    
    //test, move right
    ledmdata.moffset += 10;
    if ((ledmdata.moffset/8) * ledmdata.fontsize >= ledmdata.datanum) 
      ledmdata.moffset =0;
    //print the screen
    for(j=0; j<64; j++) { //each row
      for(i=0; i<128; i++) { //each line
        tmpmdata = screenmdata[(i/8) * 64 +j];
        if(tmpmdata & (0x01 << (7-(i%8)))){ //if the bit is 1 
          //print 0
          printf("*");
        }
    
        else {
          //print 0
          printf("-");
        }
      }
      printf("\n");
    }
    
    

    printf("%d data update in send thread\n", updatenum);
    sleep(1);
  }
}

int main(void)
{
  CURL *curl_handle;
  CURLcode res;
  char* pd[40]; //the point of each ,
  int dn, pn; 
  
  tramdata.datanum = 0;
  
  pthread_t th_a;  //create the new thread

  struct MemoryStruct chunk;

  chunk.memory = malloc(1);  /* will be grown as needed by the realloc above */
  chunk.size = 0;    /* no data at this point */
  
  /***************************************************
  seting USB
  ***************************************************/
  int rc;
  while(1) {
    /* Initialize libusb*/
    rc = libusb_init(NULL);
    if (rc < 0) {
      fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc));
      sleep(3);
      continue;
    }

    /* Set debugging output to max level.
     */
    libusb_set_debug(NULL, 3);

    /* Look for a specific device and open it.
     */
    devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
    if (!devh) {
      fprintf(stderr, "Error finding USB device\n");
      sleep(3);
      continue;
    }

    /* As we are dealing with a CDC-ACM device, it's highly probable that
     * Linux already attached the cdc-acm driver to this device.
     * We need to detach the drivers from all the USB interfaces. The CDC-ACM
     * Class defines two interfaces: the Control interface and the
     * Data interface.
     */
    for (int if_num = 0; if_num < 2; if_num++) {
      if (libusb_kernel_driver_active(devh, if_num)) {
        libusb_detach_kernel_driver(devh, if_num);
      }
      rc = libusb_claim_interface(devh, if_num);
      if (rc < 0) {
        fprintf(stderr, "Error claiming interface: %s\n",libusb_error_name(rc));
        if (devh)  libusb_close(devh);
        sleep(3);
        continue;
      }
    }

    /* Start configuring the device:
     * - set line state
     */
    rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS,
                                0, NULL, 0, 0);
    if (rc < 0) {
        fprintf(stderr, "Error during control transfer: %s\n", libusb_error_name(rc));
    }

    /* - set line encoding: here 9600 8N1
     * 9600 = 0x2580 ~> 0x80, 0x25 in little endian
     */
    unsigned char encoding[] = { 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };
    rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding, sizeof(encoding), 0);
    if (rc < 0) {
      fprintf(stderr, "Error during control transfer: %s\n",
                libusb_error_name(rc));
    }

    break;
  }
  
  pthread_mutex_init(&mutmdata, NULL); //init the mutex
  pthread_create(&th_a,NULL,thr_send_date,0);

  curl_global_init(CURL_GLOBAL_ALL);

  /* init the curl session */
  curl_handle = curl_easy_init();

  /* specify URL to get */
  curl_easy_setopt(curl_handle, CURLOPT_URL, "http://hq.sinajs.cn/list=sz002121");

  /* send all data to this function  */
  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);

  /* we pass our 'chunk' struct to the callback function */
  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
  
  curl_easy_setopt( curl_handle, CURLOPT_CONNECTTIMEOUT, 3 );
  curl_easy_setopt( curl_handle, CURLOPT_TIMEOUT, 10 );

  /* some servers don't like requests that are made without a user-agent
     field, so we provide one */
  curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
  
  //the str number is 400 GBK2312 words
  //char size is 400*32 = 12800 so 
  struct stmdata stockmdata;

  stockmdata.startx = 14;
  stockmdata.starty = 40;  //top left
  stockmdata.endx = 110;
  stockmdata.endy = 63;   //buttom right of line1
  stockmdata.mod = 1;    // 0 static; 1 runleft
  stockmdata.moffset = 16;//offset
  stockmdata.fontsize = 16; //fontsize 16
  stockmdata.halign = 0;
  stockmdata.valign = 1;

   
  
  while(1) {

  /* get it! */
  res = curl_easy_perform(curl_handle);

  /* check for errors */
  if(res != CURLE_OK) {
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(res));
  }
  else {
    /*
     * Now, our chunk.memory points to a memory block that is chunk.size
     * bytes big and contains the remote file.
     *
     * Do something nice with it!
     */
     //if chunk.match == 0, the data not update
    if(!chunk.match) {printf("Not update!\n");sleep(5);continue;}
    
    //search the whole chunk.memory, if the value is "," then record 
    //the pointer to pd[pn]
    for(dn=0, pn=0; dn<chunk.size; dn++){
      if((*(chunk.memory + dn))== ','){
        pd[pn] = chunk.memory + dn;
        pn++;
      }
    }

    printf("%lu bytes retrieved\n", (long)chunk.size);
    printf("%d , found\n", pn);
    
    stockmdata.datanum = 0;
    
    for(dn = -8; dn < 0; dn+=2){
      unsigned int tmpzhk16 = ((unsigned int)(*(pd[0] + dn))<<8) & 0xff00;
      tmpzhk16 = tmpzhk16 | ((unsigned int)(*(pd[0] + dn +1)) & 0xff);
      addmdata(tmpzhk16, &stockmdata);
    }
    printf("股票代码：");
    //add the zhk16 data to mdate
    addmdata(0xb9c9, &stockmdata);
    addmdata(0xc6b1, &stockmdata);
    addmdata(0xb4fa, &stockmdata);
    addmdata(0xc2eb, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = -18; dn < -10; dn++){
      printf("%c", *(pd[0] + dn));
      addmdata((unsigned int)(*(pd[0] + dn)), &stockmdata);
    }
    printf("\n");
    
    pn = 0;
    printf("今开：");
    addmdata(0xbdf1, &stockmdata);
    addmdata(0xbfaa, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
      addmdata((unsigned int)(*(pd[pn] + dn)), &stockmdata);
    }
    pn++;
    printf("\n");
    
    printf("昨收：");
    addmdata(0xd7f2, &stockmdata);
    addmdata(0xcad5, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
      addmdata((unsigned int)(*(pd[pn] + dn)), &stockmdata);
    }
    pn++;
    printf("\n");
    
    printf("当前：");
    addmdata(0xb5b1, &stockmdata);
    addmdata(0xc7b0, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
      addmdata((unsigned int)(*(pd[pn] + dn)), &stockmdata);
    }
    pn++;
    printf("\n");
    
    printf("最高：");
    addmdata(0xd7ee, &stockmdata);
    addmdata(0xb8df, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
      addmdata((unsigned int)(*(pd[pn] + dn)), &stockmdata);
    }
    pn++;
    printf("\n");
    
    printf("最低：");
    addmdata(0xd7f2, &stockmdata);
    addmdata(0xb5cd, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
      addmdata((unsigned int)(*(pd[pn] + dn)), &stockmdata);
    }
    pn++;
    printf("\n");
    
    printf("竞买：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("竞卖：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("成交手：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("成交额：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("买一：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("买二：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
     printf("买三：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
     printf("买四：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
     printf("买五：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
     printf("卖一：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("卖二：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("卖三：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("卖四：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("卖五：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf(" 价格 ");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("日期：");
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
    }
    pn++;
    printf("\n");
    
    printf("时间：");
    addmdata(0xcab1, &stockmdata);
    addmdata(0xbce4, &stockmdata);
    addmdata(':', &stockmdata);
    addmdata(' ', &stockmdata);
    for(dn = 1; (pd[pn] + dn) < pd[pn+1]; dn++){
      printf("%c", *(pd[pn] + dn));
      addmdata((unsigned int)(*(pd[pn] + dn)), &stockmdata);
    }
    pn++;
    printf("  %d is putin mdata", stockmdata.datanum);
    printf("\n");
    
    //trans data
    pthread_mutex_lock(&mutmdata); 
    memcpy(&tramdata, &stockmdata, sizeof(tramdata));
    pthread_mutex_unlock(&mutmdata);
    
  }
    sleep(5);
}

  /* cleanup curl stuff */
  curl_easy_cleanup(curl_handle);

  if(chunk.memory)
    free(chunk.memory);

  /* we're done with libcurl, so clean it up */
  curl_global_cleanup();

  return 0;
}
